package org.xbmc.android.xbmcremote;

import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.List;

import org.xbmc.eventclient.Packet;
import org.xbmc.eventclient.PacketHELO;
import org.xbmc.eventclient.PacketNOTIFICATION;
import org.xbmc.eventclient.PacketPING;
import org.xbmc.eventclient.XBMCClient;


import android.app.Application;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.net.wifi.ScanResult;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.Toast;


public class XBMCApplication extends Application implements OnSharedPreferenceChangeListener {
	private HashMap<Integer, XBMCEventMapping> oEventMapping = new HashMap<Integer, XBMCEventMapping>();
	private XBMCEventMapping oTouchXBMCEventMapping;
	private XBMCEventMapping oIncommingCallXBMCEventMapping;
	
	private XBMCClient oXBMCClient;
	private int xXBMC = 0;
	private int yXBMC = 0;
	private boolean isWifiConnected = false;
	
	private InetAddress mHostName;
	private int mPort;

	private SharedPreferences mSharedPreferences;
	private WifiManager mWifiManager;
	private long callTime = 0;
	private String callInfo = "";
	
	private boolean bActivityRunning = false;
	private String currentState = "Unknown";
	private byte[] iconData = null;
	private String wifiSSID;
	
	@Override
	public void onCreate() {
		super.onCreate();
    	System.err.println("onCreate Application");
    	
    	mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
    	mSharedPreferences.registerOnSharedPreferenceChangeListener(this);
    	
        // Add the Key-Mapping
    	oEventMapping.put(KeyEvent.KEYCODE_DPAD_UP, new XBMCEventMapping("KB", "up", false));
    	oEventMapping.put(KeyEvent.KEYCODE_DPAD_DOWN, new XBMCEventMapping("KB", "down", false));
    	oEventMapping.put(KeyEvent.KEYCODE_DPAD_LEFT, new XBMCEventMapping("KB", "left", false));
    	oEventMapping.put(KeyEvent.KEYCODE_DPAD_RIGHT, new XBMCEventMapping("KB", "right", false));
        oEventMapping.put(KeyEvent.KEYCODE_DPAD_CENTER, new XBMCEventMapping("KB", "return", true));
        oEventMapping.put(KeyEvent.KEYCODE_BACK, new XBMCEventMapping("KB", "escape", true));
        oEventMapping.put(KeyEvent.KEYCODE_P, new XBMCEventMapping("KB", "p", true));
        oEventMapping.put(KeyEvent.KEYCODE_Q, new XBMCEventMapping("KB", "q", true));
        oEventMapping.put(KeyEvent.KEYCODE_F, new XBMCEventMapping("KB", "f", true));
        oEventMapping.put(KeyEvent.KEYCODE_R, new XBMCEventMapping("KB", "r", true));
        oEventMapping.put(KeyEvent.KEYCODE_ENTER, new XBMCEventMapping("KB", "enter", true));
        oEventMapping.put(KeyEvent.KEYCODE_DEL, new XBMCEventMapping("KB", "backspace", true));
        oEventMapping.put(KeyEvent.KEYCODE_M, new XBMCEventMapping("KB", "m", true));
        oEventMapping.put(KeyEvent.KEYCODE_S, new XBMCEventMapping("KB", "s", true));
        oEventMapping.put(KeyEvent.KEYCODE_I, new XBMCEventMapping("KB", "i", true));
        //oKeyMapping.put(KeyEvent.KEYCODE_MENU, new XBMCKeyMapping("KB", "menu", true)); // Don't Map the Menu-Button
        oEventMapping.put(KeyEvent.KEYCODE_C, new XBMCEventMapping("KB", "c", true));
        oEventMapping.put(KeyEvent.KEYCODE_SPACE, new XBMCEventMapping("KB", "space", true));
        oEventMapping.put(KeyEvent.KEYCODE_0, new XBMCEventMapping("KB", "zero", true));
        oEventMapping.put(KeyEvent.KEYCODE_1, new XBMCEventMapping("KB", "one", true));
        oEventMapping.put(KeyEvent.KEYCODE_2, new XBMCEventMapping("KB", "two", true));
        oEventMapping.put(KeyEvent.KEYCODE_3, new XBMCEventMapping("KB", "three", true));
        oEventMapping.put(KeyEvent.KEYCODE_4, new XBMCEventMapping("KB", "for", true));
        oEventMapping.put(KeyEvent.KEYCODE_5, new XBMCEventMapping("KB", "five", true));
        oEventMapping.put(KeyEvent.KEYCODE_6, new XBMCEventMapping("KB", "six", true));
        oEventMapping.put(KeyEvent.KEYCODE_7, new XBMCEventMapping("KB", "seven", true));
        oEventMapping.put(KeyEvent.KEYCODE_8, new XBMCEventMapping("KB", "eight", true));
        oEventMapping.put(KeyEvent.KEYCODE_9, new XBMCEventMapping("KB", "nine", true));
        oEventMapping.put(KeyEvent.KEYCODE_BACKSLASH, new XBMCEventMapping("KB", "backslash", true));
        oEventMapping.put(KeyEvent.KEYCODE_VOLUME_UP, new XBMCEventMapping("KB", "volume_up", true));
        oEventMapping.put(KeyEvent.KEYCODE_VOLUME_DOWN, new XBMCEventMapping("KB", "volume_down", true));
        oEventMapping.put(KeyEvent.KEYCODE_CAMERA, new XBMCEventMapping("KB", "tab", true));
        oEventMapping.put(KeyEvent.KEYCODE_ENDCALL, new XBMCEventMapping("KB", "s", true));
        oEventMapping.put(KeyEvent.KEYCODE_X, new XBMCEventMapping("KB", "x", true));

        oTouchXBMCEventMapping = new XBMCEventMapping("KB", "enter", false);
        oIncommingCallXBMCEventMapping = new XBMCEventMapping("XBMC.Action(pause)");
        
        try{
	        InputStream iconStream = getResources().openRawResource(R.drawable.android);
			iconData = new byte[iconStream.available()];
			iconStream.read(iconData);
		
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
        
        mWifiManager = (WifiManager)getSystemService(WIFI_SERVICE);
        WifiInfo info = mWifiManager.getConnectionInfo();
        boolean newIsWifiConnected = false;
        String newWifiSSID = "";
		setCurrentState("Enable Wi-Fi to make use of this Application");
        if(info!=null)
        {
        	newWifiSSID = info.getSSID();
        	if(info.getSupplicantState()==SupplicantState.COMPLETED)
        		newIsWifiConnected = true;
        }
        setWifiConnected(newIsWifiConnected, newWifiSSID);
        
	}
	
	public void startEventClient()
	{	
		// Check if WiFi based Connectionsettings are avilable...
		if (mSharedPreferences.getString(wifiSSID+"host", "").equals("")) 
		{
			Intent i = new Intent(this, WifiConnectionSettings.class);
			i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
			startActivity(i);

			setCurrentState("No Settings found for Wi-Fi "+wifiSSID+"!");
			showCurrentState();
			return;
		}
		
        // Application created, connect to XBMC...
		try {
			String hostName = mSharedPreferences.getString(wifiSSID+"host", "");
			try {
				mPort = Integer.parseInt(mSharedPreferences.getString(wifiSSID+"port", "9777"));
			} catch (NumberFormatException e)
			{
				mPort= 9777;
			}
			
			mHostName = InetAddress.getByName(hostName);
			stopEventClient(); // If its still running...
			oXBMCClient = new XBMCClient(mHostName, mPort, "Android", Packet.ICON_PNG, iconData);
			//callTime = System.currentTimeMillis(); //fake it!
			//callInfo = "Blabal";
			if((System.currentTimeMillis()-callTime) < 15000)
			{
				// Often the phone call wakes WIFI up, therefore we have to look if the last call is less than, 
				// lets say, 15 seconds old. If yes, process it...
				sendPhoneCall();
			}
			
			setCurrentState("Connection succeeded to Host "+hostName);
			showCurrentState();
			
			
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	}
	
	public void setCurrentState(String currentState)
	{
		this.currentState = currentState;
	}
	
	public void showCurrentState()
	{
		if(bActivityRunning)
			Toast.makeText(this, currentState, Toast.LENGTH_SHORT).show();
		else
			Toast.makeText(this, "XBMC Remote: "+currentState, Toast.LENGTH_SHORT).show();
	}
	
	public void setActivityRunning(boolean newState)
	{
		bActivityRunning = newState;
	}
	
	public void stopEventClient()
	{
		if(oXBMCClient==null)
			return;
		try {
			oXBMCClient.stopClient();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public String getSSIDfromBSSID(String bssid)
	{
		List<ScanResult> scanResultList = mWifiManager.getScanResults();
		for(ScanResult scanResult : scanResultList)
		{
			if(scanResult.BSSID.equals(bssid))
			{
				return scanResult.SSID;
			}	
		}
		return null;
	}
	
	public void setWifiConnected(boolean isWifiConnected, String ssid)
	{
		if(ssid !=null)
			wifiSSID = ssid;
		if(this.isWifiConnected != isWifiConnected)
		{
			this.isWifiConnected = isWifiConnected;
			if(isWifiConnected)
			{
				wifiSSID = ssid;
				Log.d("XBMCRemote", "Connected to " + wifiSSID);

				// Altough, network says its connected, we have to wait some time
				// else UDP seems to fail to deliver the first package....
				/*
				try {
					Thread.sleep(0);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				*/
				startEventClient();
			}
			else
			{
				setCurrentState("Enable Wi-Fi to make use of this Application");
				showCurrentState();
				stopEventClient();
			}
		}
	}


	public boolean processKeyDown(int keyCode)
	{
		if(oXBMCClient==null)
			return false;
    	XBMCEventMapping oXBMCKeyMapping = oEventMapping.get(keyCode);
    	if(oXBMCKeyMapping == null)
    		return false;

    	return oXBMCKeyMapping.sendButton(oXBMCClient, true);
	}
	
	public boolean processKeyUp(int keyCode)
	{
		if(oXBMCClient==null)
			return false;
    	XBMCEventMapping oXBMCKeyMapping = oEventMapping.get(keyCode);
    	if(oXBMCKeyMapping == null)
    		return false;

    	return oXBMCKeyMapping.sendButton(oXBMCClient, false);
	}
	
	public boolean processTouchPress()
	{
		if(oXBMCClient==null)
			return false;
		return oTouchXBMCEventMapping.sendButton(oXBMCClient, true);
	}

    
	
	@Override
	public void onTerminate() {
		super.onTerminate();
    	System.err.println("onTerminate Application");
	}
	

	public void touchDifference(int x, int y) 
	{
		if(oXBMCClient==null)
			return;
		xXBMC -= x;
		yXBMC -= y;
		if(xXBMC>65535)
			xXBMC = 65535;
		if(xXBMC<0)
			xXBMC = 0;
		if(yXBMC>65535)
			yXBMC = 65535;
		if(yXBMC<0)
			yXBMC = 0;
		try {
			oXBMCClient.sendMouse(xXBMC, yXBMC);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

	public void phoneCall(String string) {
		this.callInfo = string;
		this.callTime = System.currentTimeMillis();
		if(oXBMCClient==null)
			return;

		sendPhoneCall();
	}
	
	public void sendPhoneCall()
	{
		Thread test = new Thread(new Runnable() {

			@Override
			public void run() {
				try {
					for(int i = 0;i<5;i++)
					{
						PacketPING ping = new PacketPING();
						ping.send(mHostName, mPort);
						try {
							Thread.sleep(10);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
					oXBMCClient.sendNotification("Incoming call", callInfo);
					oIncommingCallXBMCEventMapping.sendButton(oXBMCClient, true);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}	
			}
		});
		test.start();
	}
	

	@Override
	public void onSharedPreferenceChanged(SharedPreferences arg0, String arg1) {
		startEventClient();
	}
}
